﻿using System.Numerics;
using System.Text;
using OpenGL;

namespace SDL2_OpenGL_ImGui;

public class Shader
{
    private readonly string[] _vertexSrc;
    private readonly string[] _fragmentSrc;
    private readonly uint _programHandle;
    public string ErrInfo { get; private set; }
    public string ProgramName { get; }
    
    public Shader(string programName, string vertexSource, string fragmentSource)
    {
        _vertexSrc = new []{vertexSource};
        _fragmentSrc = new []{fragmentSource};
        ProgramName = programName;
        _programHandle = Gl.CreateProgram();
        ErrInfo = "";
    }

    ~Shader()
    {
        Gl.DeleteProgram(_programHandle);
    }

    public bool Build()
    {
        var vShader = Gl.CreateShader(ShaderType.VertexShader);
        var fShader = Gl.CreateShader(ShaderType.FragmentShader);
        var strBuild = new StringBuilder();
        {
            Gl.ShaderSource(vShader, _vertexSrc);
            Gl.CompileShader(vShader);
            Gl.GetShader(vShader, ShaderParameterName.CompileStatus, out var success);
            if (success == 0)
            {
                //获取错误信息
                Gl.GetShaderInfoLog(vShader, int.MaxValue, out _, strBuild);
                ErrInfo = strBuild.ToString();
                return false;
            }

            Gl.AttachShader(_programHandle, vShader);
        }

        {
            Gl.ShaderSource(fShader, _fragmentSrc);
            Gl.CompileShader(fShader);
            Gl.GetShader(fShader, ShaderParameterName.CompileStatus, out var success);
            if (success == 0)
            {
                //获取错误信息
                Gl.GetShaderInfoLog(fShader, int.MaxValue, out _, strBuild);
                ErrInfo = strBuild.ToString();
                return false;
            }
            
            Gl.AttachShader(_programHandle, fShader);
        }

        {
            Gl.LinkProgram(_programHandle);
            Gl.GetProgram(_programHandle, ProgramProperty.LinkStatus, out var success);
            if (success == 0)
            {
                //获取错误信息
                Gl.GetProgramInfoLog(_programHandle, 1024, out _, strBuild);
                ErrInfo = strBuild.ToString();
                return false;
            }
        }
        
        Gl.DeleteShader(vShader);
        Gl.DeleteShader(fShader);
        return true;
    }

    public void UseShader()
    {
        Gl.UseProgram(_programHandle);
    }

    public void SetInt(string name, int v)
    {
        Gl.Uniform1i(Gl.GetUniformLocation(_programHandle, name), 1, v);
    }
    public void SetFloat(string name, float v)
    {
        Gl.Uniform1f(Gl.GetUniformLocation(_programHandle, name), 1, v);
    }
    public void SetMat4(string name, Matrix4x4 v)
    {
        Gl.UniformMatrix4(Gl.GetUniformLocation(_programHandle, name), false, new []
        {
            v.M11, v.M12, v.M13, v.M14,
            v.M21, v.M22, v.M23, v.M24,
            v.M31, v.M32, v.M33, v.M34,
            v.M41, v.M42, v.M43, v.M44,
        });
    }
}